//
//  RACFoundationViewController.m
//  FRP_OC
//
//  Created by DeLong Yang on 2017/8/7.
//  Copyright © 2017年 DeLongYang. All rights reserved.
/*
    Reactive Cocoa  结合了 函数式 响应式 和 链式编程的思想
    1.0 链式编程  的典范 是 Masonry
 */

#import "RACFoundationViewController.h"
#import "NSObject+Calculate.h"   // yin ru calculate
#import "Person.h"
#import "FuntionalProgram.h"  // funtional program
#import "SQLTool.h"


#import <ReactiveObjC/ReactiveObjC.h>
#import "TwoViewController.h"    //
#import "FlagItem.h"    //
#import "RedView.h"     //
#import "NSArray+DLArrayExtension.h"   // 加上的 arrayExtension
#import "DLJSONModel.h"

@interface RACFoundationViewController ()

// test signal send and received
@property (nonatomic,strong)RACSubject *subject;

// test raplaySubject
@property (nonatomic,strong)RACReplaySubject *replaySubject;

// command
@property (nonatomic,strong)RACCommand *command;

@property (weak, nonatomic) IBOutlet UITextField *textField;

@property (weak, nonatomic) IBOutlet UIButton *btn;

@property (weak, nonatomic) IBOutlet UILabel *lableView;


@end

@implementation RACFoundationViewController
{
    
    RedView *redV;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
//    [self compareChainProgram];
//    [self functionalPrograming];
//
//    //
//    [self simpleRacSignalUse];
////    //
//    [self testRacReplaySubject];
    
//    [self racSignalTest];
    
    //  simultate  RAC
    [self simulateRACFunctional];
    
//
//    // show the special data in rac
//    [self dictionaryIntoModel];
    
    // rac
//    [self raccommandTest];
    
//    [self testOriginalWeSeeMutiRequest];
    
    //
//    [self testRACMultucastConnection];
    
    //
    redV = [[RedView alloc] initWithFrame:CGRectMake(20, 200, 200, 100)];
    [self.view addSubview:redV];
    
    [self racMethodsTest];
//
//    // test redV  change center
//    [self rac_Macro_test];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)dealloc
{
    NSLog(@"racfoundation dealloc");
}


#pragma mark ---- 演示 链式编程
// 博客的讲解的链式 编程 有问题
//- (void)testChainPrograming
//{
//    int result = [NSObject makeCaculators:^(CalculatorMaker *make) {
//        make.add(3);
//    }];
//}

// 对比 链式 和 非链式的 区别
- (void)compareChainProgram
{
    // 一般的写法
    Person *person = [[Person alloc]init];
    [person eat1];  // 后面没法继续
    [[person eat2] sleep2];
    
    //
    person.eat3();
    person.sleep3();  //
    
    //
    person.eat4().sleep4();
    
    //
    person.eat5(@"cake").sleep5(4);
    
    // rac signal test
}

//
// 函数式编程 
- (void)functionalPrograming
{
    FuntionalProgram *func = [[FuntionalProgram alloc]init];
    func.result = 1;
    
    //   这个是函数式  编程的例子
    BOOL isequl = [[[func calulate:^int(int result) {
        result += 1;
        result *= 5;
        return result;
    }] equal:^BOOL(int result) {
        return result == 10;
    }] isEqual];
    
    NSLog(@"isEqual is %d",isequl);
    
}

#pragma mark ---- 模仿 RAC 的实现
- (void)simulateRACFunctional
{
    // 在OC 中指定数组的元素为指定的类型
    NSArray <NSNumber *> *numbers = @[@10, @15, @99, @66, @25, @28.1, @7.5, @11.2, @66.2];
    NSArray *result = numbers.filter(^BOOL(NSNumber *item){
        return item.doubleValue > 20;
    }).map(^id(NSNumber *item){
        return [NSString stringWithFormat:@"string %g",item.doubleValue];
    });
    NSLog(@"result is %@",result);
    
    //  jsons 的数组
    NSArray <NSDictionary *> *jsons = @[@{@"key":@"DeLong"},@{@"key":@"Yang"},@{@"key":@"Hello"}];
    NSArray <DLJSONModel *>*models = jsons.map(^(id item){
        return [[DLJSONModel alloc] initWithJSON:item];
    });
    NSLog(@"models is %@",models);
}


/*
 3.4 响应式编程思想：不需要考虑调用顺序，只需要知道考虑结果，类似于蝴蝶效应，产生一个事件，会影响很多东西，这些事件像流一样的传播出去，然后影响结果，借用面向对象的一句话，万物皆是流。
 
 代表：KVO运用。
 */
#pragma mark ---- RACSignal Use

// simple test rac signal
- (void)racSignalTest
{
    // RACSignal使用步骤：
    // 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
    // 2.订阅信号,才会激活信号. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
    // 3.发送信号 - (void)sendNext:(id)value
    
    // 1.创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        // block调用时刻：每当有订阅者订阅信号，就会调用block。
        // 2.发送信号
        [subscriber sendNext:@"HelloDeLong"];
        [subscriber sendNext:@"this is the final message"];
        [subscriber sendCompleted];
        
        return [RACDisposable disposableWithBlock:^{
            
            // block调用时刻：当信号发送完成或者发送错误，就会自动执行这个block,取消订阅信号。
            // 执行完Block后，当前信号就不在被订阅了。
            NSLog(@"destroy signal");
        }];
    }];
    
    // 3.订阅信号,才会激活信号.
    [signal subscribeNext:^(id x) {
        // block调用时刻：每当有信号发出数据，就会调用block.
        NSLog(@"接收到数据:%@",x);
    }];
    
    /*
     
     6.2 RACSubscriber:表示订阅者的意思，用于发送信号，这是一个协议，不是一个类，只要遵守这个协议，并且实现方法才能成为订阅者。通过create创建的信号，都有一个订阅者，帮助他发送数据。
     
     6.3 RACDisposable:用于取消订阅或者清理资源，当信号发送完成或者发送错误的时候，就会自动触发它。
     使用场景:不想监听某个信号时，可以通过它主动取消订阅信号。
     */
}

/*
 所以，你可能听说过ReactiveCocoa被描述为函数响应式编程（FRP）框架。
 
 以后使用RAC解决问题，就不需要考虑调用顺序，直接考虑结果，把每一次操作都写成一系列嵌套的方法中，使代码高聚合，方便管理。
 6.4 RACSubject:RACSubject:信号提供者，自己可以充当信号，又能发送信号。
 
 使用场景:通常用来代替代理，有了它，就不必要定义代理了。
 RACReplaySubject:重复提供信号类，RACSubject的子类。
 
 RACReplaySubject与RACSubject区别:
 RACReplaySubject可以先发送信号，在订阅信号，RACSubject就不可以。
 使用场景一:如果一个信号每被订阅一次，就需要把之前的值重复发送一遍，使用重复提供信号类。
 使用场景二:可以设置capacity数量来限制缓存的value的数量,即只缓充最新的几个值。
 
 RACSubject和RACReplaySubject简单使用:
 
*/

/**
  show the use of racsignalUse
  RACSubject使用步骤
  1.创建信号 [RACSubject subject]，跟RACSiganl不一样，创建信号时没有block。
  2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
  3.发送信号 sendNext:(id)value
 
  RACSubject:底层实现和RACSignal不一样。
  1.调用subscribeNext订阅信号，只是把订阅者保存起来，并且订阅者的nextBlock已经赋值了。
  2.调用sendNext发送信号，遍历刚刚保存的所有订阅者，一个一个调用订阅者的nextBlock。

 */
- (void)simpleRacSignalUse
{
    //  一个subject
    self.subject = [RACSubject subject];
    
    // 改变 subject 的顺序 以发现信号的执行过程
//    [self.subject sendNext:@(1)];
//    [self.subject sendNext:@(2)];
    
    [self.subject subscribeNext:^(id  _Nullable x) {
        // block调用时刻：当信号发出新值，就会调用.
        NSLog(@"第一个订阅者%@",x);
    }];
    
    //
    [self.subject subscribeNext:^(id x) {
        // block调用时刻：当信号发出新值，就会调用.
        NSLog(@"第二个订阅者%@",x);
    }];
    
    [self.subject sendNext:@(1)];
    [self.subject sendNext:@(2)];
    
}

- (IBAction)onSimpleMsgBtnClicked:(id)sender
{
    [self.subject sendNext:@"1"];
}


/*
  RACReplaySubject Use
  RACReplaySubject使用步骤:
  1.创建信号 [RACSubject subject]，跟RACSiganl不一样，创建信号时没有block。
  2.可以先订阅信号，也可以先发送信号。
  2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
  2.2 发送信号 sendNext:(id)value
 
  RACReplaySubject:底层实现和RACSubject不一样。
  1.调用sendNext发送信号，把值保存起来，然后遍历刚刚保存的所有订阅者，一个一个调用订阅者的nextBlock。
  2.调用subscribeNext订阅信号，遍历保存的所有值，一个一个调用订阅者的nextBlock
 
  如果想当一个信号被订阅，就重复播放之前所有值，需要先发送信号，在订阅信号。
  也就是先保存值，在订阅值。
 
 */
- (void)testRacReplaySubject
{
    self.replaySubject = [RACReplaySubject subject];
    [self.replaySubject sendNext:@(1)];
    [self.replaySubject sendNext:@(2)];
    
    // 注意订阅之前发布的 消息 会一个 订阅者 一个订阅者来
    [self.replaySubject subscribeNext:^(id  _Nullable x) {
        // block调用时刻：当信号发出新值，就会调用.
        NSLog(@" reapply 第一个订阅者%@",x);
    }];
    
    //
    [self.replaySubject subscribeNext:^(id x) {
        // block调用时刻：当信号发出新值，就会调用.
        NSLog(@"reapply  第二个订阅者%@",x);
    }];
    
}

//
- (IBAction)replaySubjectClicked:(id)sender
{
    [self.replaySubject sendNext:@(3)];
}

#pragma mark ---  use subject to raplace delegate
//
- (IBAction)nextTwoClick:(id)sender
{
    TwoViewController *twoVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"123"];
    //
    twoVC.delegateSignal = [RACSubject subject];
    
    [twoVC.delegateSignal subscribeNext:^(id  _Nullable x) {
       
        NSLog(@"receive two viewcontroller msg %@",x);
    }];
    
    [self.navigationController pushViewController:twoVC animated:YES];
    
}

#pragma mark ----  mode tranfer into dictionary  !!!  并不完善 只是 读取相应的元素

/*
 6.6RACTuple:元组类,类似NSArray,用来包装值.
 
 6.7RACSequence:RAC中的集合类，用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
 
 使用场景：1.字典转模型
 
 RACSequence和RACTuple简单使用
 */
// dic into model
- (void)dictionaryIntoModel
{
    NSArray *numbers = @[@1,@2,@3,@4];
    // 这里其实是三步
    // 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
    // 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
    // 第三步: 订阅信号，激活信号，会自动把集合中的所有值，遍历出来。
    [numbers.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
         NSLog(@"%@",x);
    }];
    
    // 2.遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)
    NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
    [dict.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
          // 解包元组，会把元组的值，按顺序给参数里面的变量赋值
        RACTupleUnpack(NSString *key,NSString *value) = x;
        NSLog(@"%@ %@",key,value);
        
    }];
    
    // infact this is no need to have a plist we can create a dic
    NSDictionary *dict2 = @{@"name":@"xmg",@"age":@18,@"score":@88.6};
    // with dic

    
    
}


#pragma mark ----  RACCommand
/*
 6.8RACCommand:RAC中用于处理事件的类，可以把事件如何处理,事件中的数据如何传递，包装到这个类中，他可以很方便的监控事件的执行过程。
 使用场景:监听按钮点击，网络请求
 RACCommand简单使用
  一、RACCommand使用步骤:
  1.创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
  2.在signalBlock中，创建RACSignal，并且作为signalBlock的返回值
  3.执行命令 - (RACSignal *)execute:(id)input
 
  二、RACCommand使用注意:
  1.signalBlock必须要返回一个信号，不能传nil.
  2.如果不想要传递信号，直接创建空的信号[RACSignal empty];
  3.RACCommand中信号如果数据传递完，必须调用[subscriber sendCompleted]，这时命令才会执行完毕，否则永远处于执行中。
  4.RACCommand需要被强引用，否则接收不到RACCommand中的信号，因此RACCommand中的信号是延迟发送的。
 
  三、RACCommand设计思想：内部signalBlock为什么要返回一个信号，这个信号有什么用。
  1.在RAC开发中，通常会把网络请求封装到RACCommand，直接执行某个RACCommand就能发送请求。
  2.当RACCommand内部请求到数据的时候，需要把请求的数据传递给外界，这时候就需要通过signalBlock返回的信号传递了。
 
  四、如何拿到RACCommand中返回信号发出的数据。
  1.RACCommand有个执行信号源executionSignals，这个是signal of signals(信号的信号),意思是信号发出的数据是信号，不是普通的类型。
  2.订阅executionSignals就能拿到RACCommand中返回的信号，然后订阅signalBlock返回的信号，就能获取发出的值。
 
  五、监听当前命令是否正在执行executing
 
  六、使用场景,监听按钮点击，网络请求
 */

/**
 simple test comand
 */
- (void)raccommandTest
{
    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        NSLog(@"执行命令");
        
        // 创建空信号,必须返回信号
        //        return [RACSignal empty];
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            // we send three msg
            [subscriber  sendNext:@"requsetData"];
            [subscriber sendNext:@"second msg"];
            [subscriber sendNext:@"three msg"];
            [subscriber sendCompleted];
            
            return nil;
        }];
    }];
    
    // 强引用命令，不要被销毁，否则接收不到数据
    self.command = command;
    
    // 3.订阅RACCommand中的信号
    [command.executionSignals subscribeNext:^(id  _Nullable x) {
        [x subscribeNext:^(id x) {
            
            // 这里我们没法区分 信号的顺序 因为信号
            NSLog(@"---- %@",x);
        }];
    }];
    
    // RAC高级用法
    // switchToLatest:用于signal of signals，获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号
    // 并没有 跳过 第一个 signal
    [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"executionSignals  swithc to latest %@",x);
    }];
    
    //
    [[command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
       
        if ([x boolValue] == YES) {
            NSLog(@"command executing ");
        }else{
            //
            NSLog(@"commamd complete");
        }
    }];
    
    // this one is need to send signal  不加就没有信号出来 也就是前面的 三个信号
    [self.command execute:@"this is command from delong"];
}


#pragma mark ----  RACMulticastConnection   test

// 需求：假设在一个信号中发送请求，每次订阅一次都会发送请求，这样就会导致多次请求。
// 解决：使用RACMulticastConnection就能解决.
- (void)testOriginalWeSeeMutiRequest
{
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        NSLog(@"发送 请求");
        return nil;
    }];
    
    // 2.订阅信号
    [signal subscribeNext:^(id x) {
        
        NSLog(@"接收数据");
        
    }];
    // 2.订阅信号
    [signal subscribeNext:^(id x) {
        
        NSLog(@"接收数据");
        
    }];
    
    // 3.运行结果，会执行两遍发送请求，也就是每次订阅都会发送一次请求
}


/*
  RACMulticastConnection使用步骤:
  1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
  2.创建连接 RACMulticastConnection *connect = [signal publish];
  3.订阅信号,注意：订阅的不在是之前的信号，而是连接的信号。 [connect.signal subscribeNext:nextBlock]
  4.连接 [connect connect]
 
  RACMulticastConnection底层原理:
  1.创建connect，connect.sourceSignal -> RACSignal(原始信号)  connect.signal -> RACSubject
  2.订阅connect.signal，会调用RACSubject的subscribeNext，创建订阅者，而且把订阅者保存起来，不会执行block。
  3.[connect connect]内部会订阅RACSignal(原始信号)，并且订阅者是RACSubject
  3.1.订阅原始信号，就会调用原始信号中的didSubscribe
  3.2 didSubscribe，拿到订阅者调用sendNext，其实是调用RACSubject的sendNext
  4.RACSubject的sendNext,会遍历RACSubject所有订阅者发送信号。
  4.1 因为刚刚第二步，都是在订阅RACSubject，因此会拿到第二步所有的订阅者，调用他们的nextBlock

 */
- (void)testRACMultucastConnection
{
    // RACMulticastConnection:解决重复请求问题
    // 1.创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"connection 发送请求");
        [subscriber sendNext:@"this  is the request data "];
        return nil;
    }];
    
    // 2.创建连接
    RACMulticastConnection *connection = [signal publish];
    
    // 3.订阅信号，
    // 注意：订阅信号，也不能激活信号，只是保存订阅者到数组，必须通过连接,当调用连接，就会一次性调用所有订阅者的sendNext:
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"signal subscribe next one %@",x);
    }];
    
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"signal subscribe next two %@",x);
    }];
    
    
    // 4.连接,激活信号
    [connection connect];
}

/*
 6.10 RACScheduler:RAC中的队列，用GCD封装的。
 
 6.11 RACUnit :表⽰stream不包含有意义的值,也就是看到这个，可以直接理解为nil.
 
 6.12 RACEvent: 把数据包装成信号事件(signal event)。它主要通过RACSignal的-materialize来使用，然并卵。
 
 7.ReactiveCocoa开发中常见用法。
 
 7.1 代替代理:
 
 rac_signalForSelector：用于替代代理。
 7.2 代替KVO :
 
 rac_valuesAndChangesForKeyPath：用于监听某个对象的属性改变。
 7.3 监听事件:
 
 rac_signalForControlEvents：用于监听某个事件。
 7.4 代替通知:
 
 rac_addObserverForName:用于监听某个通知。
 7.5 监听文本框文字改变:
 
 rac_textSignal:只要文本框发出改变就会发出这个信号。
 7.6 处理当界面有多次请求时，需要都获取到数据时，才能展示界面
 
 rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组)，每一个signal都至少sendNext过一次，就会去触发第一个selector参数的方法。
 使用注意：几个信号，参数一的方法就几个参数，每个参数对应信号发出的数据。
 */

#pragma mark ----  RAC  other method test 
// rac methods

/**
  测试 rac 的其他的代理方法  试验证明 使用NSNotificationCenter 用rac的方法 注册通知 并没有 内存问题
 */
- (void)racMethodsTest
{
    // 1.代替代理
    // 需求：自定义redView,监听红色view中按钮点击
    // 之前都是需要通过代理监听，给红色View添加一个代理属性，点击按钮的时候，通知代理做事情
    // rac_signalForSelector:把调用某个对象的方法的信息转换成信号，就要调用这个方法，就会发送信号。
    // 这里表示只要redV调用btnClick:,就会发出信号，订阅就好了。
    
    [[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id  _Nullable x) {
        NSLog(@"red btn clicked ");
    }];
    
    //
    // 笔者认为： 我们需要测试 添加了KVC 或者 KVO后 是否 会造成 内存泄漏
    [[redV rac_valuesAndChangesForKeyPath:@"string" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
        RACTupleUnpack(NSString *name) = x;
        NSLog(@"name is %@",name);
        
    }];
    
    // 3.监听事件
    // 把按钮点击事件转换为信号，点击按钮，就会发送信号
    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        NSLog(@"self.btn clicked");
    }];
    
    // 4.代替通知
    // 把监听到的通知转换信号。 他这个监听 不需要移除 notificationCenter 的
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
        NSLog(@"键盘弹出");
    }];
    
    // 5.监听文本框的文字改变
    [_textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"文字改变了%@",x);
    }];
    
    // 6.处理多个请求，都返回结果的时候，统一做处理.  多个信号都收到反馈的时候 处理
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"request1"];
        return nil;
    }];
    
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"request2"];
        return nil;
    }];
    
    [self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[signal1,signal2]];
    
}

// update ui 
- (void)updateUIWithR1:(id)data r2:(id)data1
{
    NSLog(@"更新UI%@  %@",data,data1);

}

- (IBAction)changRedVClick:(id)sender
{
    NSDate *date = [NSDate date];
    redV.string = [date description];
    
}

#pragma mark ----   RAC 宏定义  常见的宏定义

//  在 RAC 中的宏。是 非常具有代表性的
/**
 主要用于测试。rac 的宏使用。
 */
- (void)rac_Macro_test
{
    // 8.1 RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
    // 只要文本框文字改变，就会修改label的文字
    RAC(self.lableView,text) = _textField.rac_textSignal;
    
    // 8.2 RACObserve(self, name):监听某个对象的某个属性,返回的是信号。  第一次加载也会调用一次
    [RACObserve(redV, center) subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    /*
     8.3  @weakify(Obj)和@strongify(Obj),一般两个都是配套使用,在主头文件(ReactiveCocoa.h)中并没有导入，
     */
//    @weakify(self);
//    @strongify(self);
    // 8.4 RACTuplePack：把数据包装成RACTuple（元组类）
    // // 把参数中的数据包装成元组
    RACTuple *tuple = RACTuplePack(@10,@20);
    
    // 8.5 RACTupleUnpack：把RACTuple（元组类）解包成对应的数据。
    //
    // 把参数中的数据包装成元组
    RACTuple *tuple2 = RACTuplePack(@"xmg",@20);
    
    // 解包元组，会把元组的值，按顺序给参数里面的变量赋值
    // name = @"xmg" age = @20
    RACTupleUnpack(NSString *name,NSNumber *age) = tuple2;
    NSLog(@"name is %@ age is %d",name,age.intValue);

    
}







@end



























































































































